package com.xdf.wxbebarrel.utils;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xdf.wxbebarrel.model.QRCodeTicketMsg;
import com.xdf.wxbebarrel.model.WechatAccessToken;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 2021年6月10日 14:45:46
 * chanchaw
 * 本工具用于请求获取二维码连接，有时效调用方法 getQRCodeTemporary 即可
 * 其他方法都是依赖方法
 * 本工具融合资料：微信公众号的 APPID、APPSECRET
 * http 的 get、post 请求
 *
 * 2022年11月28日 15:10:28 检查代码
 * 本类中的 APPID="wx47a146f7da5d3c6e" 是公众号 “精之研” 的物料，本项目使用会报错
 * 导致 /qr/tsqr 会报500错误，但本类并没有在其他地方使用
 * 所以不会造成多地分别请求新的 accessToken 导致旧的 accessToken 失效的问题。
 */
@Component
public class WxQrcodeUtils {
    private static final String APPID="wx47a146f7da5d3c6e";
    private static final String APPSECRET="0e92745e5cb924c0d887c50ee02abc1d";
    private static final String WECHAT_TOKENURL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    private static WechatAccessToken at;

    private static final ObjectMapper mapper = new ObjectMapper();

    // 传入场景值请求获取有时效的二维码
    public String getQRCodeTemporary(String scene) throws IOException {
        String ret = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + getQRTicketTemporary(scene);
        return ret;
    }


    // 传入场景值请求获取有时效的票据
    private String getQRTicketTemporary(String scene) throws IOException {
        String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN";
        String accessToken = Optional.ofNullable(getAccessToken()).orElse("");
        url = url.replace("TOKEN", accessToken);
        System.out.println("getQRTicketTemporary请求临时二维码兑换票据时用到的accessToken:" + accessToken);

        // ticket 中标记了二维码的有效期是7天 = 604800，2分钟 = 120，这里的计算单位是秒
        String data = "{\"expire_seconds\": 604800, \"action_name\": \"QR_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\":\"" + scene + "\"}}}";
        String result = postForm(url, data,false);
        StringBuilder ticket = new StringBuilder();

        QRCodeTicketMsg msg = mapper.readValue(result,QRCodeTicketMsg.class);

        if(msg != null){
            ticket.append(msg.getTicket());
        }else{
            ticket.append("");
        }
        return ticket.toString();
    }



    // 调用本方法获取 access_token
    // 如果没有可用会请求新的来
    private String getAccessToken()  {
        if(at==null||at.isExpired()) // 没有可用的 access_token，需要请求新的
            getNewAccessToken();
        return at.getAccess_token();
    }

    // 向微信服务器请求新的 access_token
    private void getNewAccessToken() {
        String url = WECHAT_TOKENURL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
        String szToken = sendGet(url);

        JavaType jvt = mapper.getTypeFactory().constructParametricType(HashMap.class,String.class,String.class);
        Map<String,String> jsonObject = null;
        try {
            jsonObject = mapper.readValue(szToken, jvt);
        }catch (IOException e){
            throw new BusinessException("请求新 token 时转换为 map 对象时出现异常！");
        }

        String access_token="";
        String expire_in = "";
        try {
            access_token = jsonObject.get("access_token");
            expire_in = jsonObject.get("expires_in");
        } catch (Exception e) {
            String errCode = jsonObject.get("errcode");
            StringBuilder sb = new StringBuilder();

            switch (errCode) {
                case "40164":
                    sb.append(jsonObject.get("errmsg"));
                    System.out.println("将IP地址添加到白名单：" + sb.toString());
                    return;
                default:
                    break;
            }
        }

        // 给全局变量赋值请求来的新的 access_token
        at = new WechatAccessToken(access_token, expire_in);
    }


    // 发送 get 请求
    private static String sendGet(String httpurl) {
        HttpURLConnection connection = null;
        InputStream is = null;
        BufferedReader br = null;
        String result = null;// 返回结果字符串
        try {
            // 创建远程url连接对象
            URL url = new URL(httpurl);
            // 通过远程url连接对象打开一个连接，强转成httpURLConnection类
            connection = (HttpURLConnection) url.openConnection();
            // 设置连接方式：get
            connection.setRequestMethod("GET");
            // 设置连接主机服务器的超时时间：15000毫秒
            connection.setConnectTimeout(15000);
            // 设置读取远程返回的数据时间：60000毫秒
            connection.setReadTimeout(60000);
            // 发送请求
            connection.connect();

            int resCode = connection.getResponseCode();
            // 通过connection连接，获取输入流
            if (connection.getResponseCode() == 200) {
                is = connection.getInputStream();
                // 封装输入流is，并指定字符集
                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                // 存放数据
                StringBuffer sbf = new StringBuffer();
                String temp = null;
                while ((temp = br.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            connection.disconnect();// 关闭远程连接
        }

        return result;
    }


    // 发送 post 请求，用于获取 QRCODE 的票据
    private static String postForm(String url, String param,boolean isproxy) {
        OutputStreamWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            HttpURLConnection conn = null;
            if(isproxy){//使用代理模式
                @SuppressWarnings("static-access")
                Proxy proxy = new Proxy(Proxy.Type.DIRECT.HTTP, new InetSocketAddress("127.0.0.1", 8888));
                conn = (HttpURLConnection) realUrl.openConnection(proxy);
            }else{
                conn = (HttpURLConnection) realUrl.openConnection();
            }
            // 打开和URL之间的连接

            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod("POST");    // POST方法


            // 设置通用的请求属性

            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//            conn.setRequestProperty("Content-Type", "application/json");
            conn.connect();

            // 获取URLConnection对象对应的输出流
            out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
            // 发送请求参数
            out.write(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }


}
